import * as d3 from "d3";
import $ from "jquery";

// function Link(uuid,predicate,sId,tId) {
//   this.uuid = uuid;
//   this.predicate = predicate;
//   this.sId = sId;
//   this.tId = tId;
// }

export function GraphPainter(gid, initInfo) {
  this.initInfo = initInfo; //初始化信息
  this.gcontainer = {};
  this.svg = {};
  this.simulation = {};
  this.isFullscreen = initInfo.isFullscreen;
  this.zoom = null;
  this.gid = gid; //绘图画布html节点id
  this.typeList = [];
  // 知识图谱数据对象
  this.kgInfo = {};
  //当前选中节点信息
  this.curNode = {
    valid: false, //当前节点是否有效（或许是上次选中信息）
    id: "",
    nodeAtId: "",
    label_ZH: "",
    type: ""
  };

  this.temp = {};
  this.theme = 0;
  this.loading = false;

  this.width = initInfo.width;
  this.height = initInfo.height;

  this.arrowMarker = {};
  this.qaGraphNode = {};
  this.qaButtonGroup = {};
  this.qaGraphNodeText = {};
  this.qaGraphLink = {};
  this.qaGraphLinkText = {};

  this.graphNode = {};
  this.graphNodeText = {};
  this.graphNodeButtonGroup = {};
  this.graphLink = {};
  this.graphLinkText = {};

  this.defaultR = 30;
  this.colorList = [
    "#F56C6C",
    "#E6A23C",
    "#67C23A",
    "#5580ff",
    "#d493ff",
    "#2f84dc",
    "#55bcff",
  ];
  this.pagesizelist = [
    { size: 100, isactive: false },
    { size: 300, isactive: false },
    { size: 500, isactive: true },
    { size: 1000, isactive: false }
  ];
  this.toolbarData = [
    { name: "编辑", value: 1, code: "editBase" },
    { name: "展开", value: 1, code: "options" },
    { name: "追加", value: 1, code: "append" },
    { name: "连线", value: 1, code: "link" },
    { name: "删除", value: 1, code: "delete" }
  ];
  this.selectUuid = 0;
  this.nodeRecordList = [];
  this.default = {
    R: 30,
    BACKGROUND_COLOR: "#F2F6FC",
    NODE_COLOR: "#409EFF",
    NODE_STROKE_COLOR: "#C0C4CC",
    MARKER_COLOR: "#909399",
    BTN_COLOR: "#909399",
    BTN_STROKE_COLOR: "#C0C4CC",
    FONT_COLOR: "#303133",
    FONT_FAMILY: "Microsoft YaHei",
    LINK_FONT_COLOR: "#606266"
  };

  let eventName = {
    curNode: "curNode",
    relatedNode: "relatedNode"
  };

  /** ----- function ----- */
  this.mapName = function() {
    let _this = this;

    let initInfo = _this.initInfo;

    let kg = JSON.parse(JSON.stringify(initInfo.kgInfo));

    function exchangeName(obj,originName,targetName) {
      if(originName === targetName) return;
      if(!obj[originName]) return;
      obj[targetName] = obj[originName];
      delete obj[originName];
    }

    //规范化节点数组与关系数组名称
    exchangeName(kg,initInfo.nodesName,"nodes");
    exchangeName(kg,initInfo.linksName,"links");
    exchangeName(kg,initInfo.kgNameName,"name");
    exchangeName(kg,initInfo.kgIdName,"id");

    //规范化节点名称
    kg.nodes.forEach(function(node) {
      exchangeName(node,initInfo.nodeIdName,"uuid");
      exchangeName(node,initInfo.nodeUriName,"uri");
      exchangeName(node,initInfo.nodeLabelName,"label");
      exchangeName(node,initInfo.nodeTypeName,"type");
      node.id = node.uuid;
    });

    //规范化连接名称
    kg.links.forEach(function(link) {
      exchangeName(link,initInfo.linkIdName,"uuid");
      exchangeName(link,initInfo.predicateName,"predicate");
      exchangeName(link,initInfo.sIdName,"sId");
      exchangeName(link,initInfo.tIdName,"tId");
    });

    //添加记录类型信息数组
    kg.typeList = [];
    _this.kgInfo = kg;
  };

  /** 图初始化入口方法 */
  this.initGraph = function() {
    context = this; //初始化GraphPainter上下文
    let _this = this;

    //--- 初始化画布 ---
    initGraphContainer();
    addMaker();

    if(new Date().getTime() >0) return ;

    this.mapName();

    //--- 规范、初始化数据 ---
    formatData();
    let nodes = _this.kgInfo.nodes;
    let links = _this.kgInfo.links;

    //--- 绘制节点 ---
    drawToolButton(); //定义按钮组引用的use元素
    drawNode(nodes); // 更新节点
    console.log("-- finish drawNode --");
    drawNodeText(nodes); // 更新节点文字
    console.log("-- finish drawNodeText --");
    drawButtonGroup(nodes); // 更新按钮组
    console.log("-- finish drawButtonGroup --");
    drawLink(links); // 更新连线 links
    console.log("-- finish drawLink --");
    drawLinkText(links); // 更新连线文字
    console.log("-- finish drawLinkText --");

    //--- 初始化simulation ---
    initSimulation(nodes, links);

    //--- 事件初始化 ---
    // 鼠标滚轮缩放
    _this.zoom = d3
      .zoom()
      .scaleExtent([0.1, 4])
      .on("zoom", zoomed);
    _this.svg.call(_this.zoom);
    // 静止双击缩放
    _this.svg.on("dblclick.zoom", null);
    //为按钮组绑定事件
    bindEventButtonGroup();
    return _this.kgInfo;
  };

  this.getNodeById = function(id) {
    return getArrItemByID(id,this.kgInfo.nodes);
  }
  this.getLinkById = function(id) {
    return getArrItemByID(id,this.kgInfo.links);
  }

  /** 编辑图节点方法 */
  this.addNode = function(uuid,uri,label,type) {
    let node = { uuid:uuid,uri:uri,label:label,type:type }
    let nodes = this.kgInfo.nodes;
    nodes.push(node);
    node.color = null;
    assignColor(node);
    d3.selectAll(".nodetext")
      .selectAll("text")
      .remove();
    d3.selectAll(".node")
      .selectAll("circle")
      .remove();
    formatData();

    nodes = this.kgInfo.nodes
    drawNode(nodes);
    drawNodeText(nodes);
    drawButtonGroup(nodes);
    restartNodesSimulation(nodes);
    refreshShow();
  };
  this.addLink = function(uuid,predicate,sId,tId) {
    let link = { uuid:uuid,predicate:predicate,sId:sId,tId:tId };
    let links = this.kgInfo.links;
    links.push(link);
    d3.selectAll(".linetext")
      .selectAll(".linktext")
      .remove();
    d3.selectAll(".line")
      .selectAll(".link")
      .remove();
    formatData();

    links = this.kgInfo.links;
    drawLink(links);
    drawLinkText(links);
    removeKgInfoOb();
    restartLinksSimulation(links);
    refreshShow();
  };
  this.editNode = function(uuid,uri,label,type) {
    let node = { uuid:uuid,uri:uri,label:label,type:type}
    node.color = null;
    assignColor(node);
    let circleList = document.querySelectorAll(".node circle");
    let circleTextList = document.querySelectorAll(".nodetext text");
    let clIndex = 0;
    let ctlIndex = 0;
    for(let i = 0;i < circleList.length;i++){
      if(circleList[i].getAttribute("uuid") == node.uuid){
        clIndex = i;
        if(circleTextList[i].getAttribute("uuid") == node.uuid){
          ctlIndex = i;
        }
        else {
          for(let j = 0;j < circleTextList.length;j++){
            if(circleTextList[j].getAttribute("uuid") == node.uuid){
              ctlIndex = j;
              break;
            }
          }
        }
        break;
      }
    }

    let curNode = this.getNodeById(node.uuid);
    curNode.uri = node.uri;
    curNode.label = node.label;
    curNode.type = node.type;

    circleList[clIndex].style.fill = node.color;
    circleTextList[ctlIndex].innerHTML = node.label + "<title>" + node.label + "</title>"
    circleTextList[ctlIndex].style.fontSize = fontSize(node) ;
    circleTextList[ctlIndex].style.fontWeight = Math.floor(900 / fontSize(node) + 500);
    circleTextList[ctlIndex].setAttribute("dy", String(fontSize(node) / 2 - 2));
  };
  this.editLink = function(uuid,predicate,sId,tId) {
    this.deleteLinkById(uuid);
    this.addLink(uuid,predicate,sId,tId);
  }
  this.deleteNodeById = function(deleteId) {
    let nodes = this.kgInfo.nodes;
    //删除当前节点
    for (let i = 0; i <= nodes.length; i++) {
      if (i == nodes.length) return;
      if (nodes[i].uuid == deleteId) {
        nodes.splice(i, 1);
        break;
      }
    }
    //删除相关连接
    let links = this.kgInfo.tripleVOS;
    let needDelLink = false;
    for (let i = 0; i < links.length; i++) {
      if (
        links[i].sId == deleteId ||
        links[i].tId == deleteId
      ) {
        links.splice(i--, 1); //删除后数组长度变短 需减1
        needDelLink = true;
      }
    }
    let data = this.formatData();
    if (needDelLink) {
      d3.selectAll(".linetext")
        .selectAll(".linktext")
        .remove();
      d3.selectAll(".line")
        .selectAll(".link")
        .remove();
      drawLink(data.links);
      drawLinkText(data.links);
    }
    drawNode(data.nodes);
    drawNodeText(data.nodes);
    drawButtonGroup(data.nodes);

    restartNodesSimulation(data.nodes);
    refreshShow();
  };
  this.deleteLinkById = function(deleteId) {
    let links = this.kgInfo.links;
    for (let i = 0; i <= links.length; i++) {
      if (i == links.length) return;
      if (links[i].uuid == deleteId) {
        links.splice(i, 1);
        break;
      }
    }
    d3.selectAll(".linetext")
      .selectAll(".linktext")
      .remove();
    d3.selectAll(".line")
      .selectAll(".link")
      .remove();
    formatData();
    links = this.kgInfo.links;
    drawLink(links);
    drawLinkText(links);
    restartLinksSimulation(links);
    refreshShow();
  };

  /** 定位到节点方法 */
  this.zoomToNode = function(id=1){
    let node = this.getNodeById(id);
    zoomToPosition(node.x,node.y);
  }
  this.zoomToLink = function(id = 1) {
    let link = this.getLinkById(id);
    let sNode = this.getNodeById(link.sId);
    let tNode = this.getNodeById(link.tId);
    zoomToPosition((sNode.x + tNode.x)/2,(sNode.y + tNode.y)/2)
  }
  function zoomToPosition(x, y) {
    let _this = context;
    _this.zoom.on("end.zoomToNode",function() {
      _this.zoom.scaleTo(_this.svg.transition().duration(100),2);
      _this.zoom.on("end.zoomToNode",null)
    })
    _this.zoom.translateTo(_this.svg.transition().duration(250),x,y);
  }

  /** 固定/释放节点方法 */
  this.fixAll = function() {
    let nodes = this.kgInfo.nodes;
    for(let i = 0;i<nodes.length;i++){
      nodes[i].fx = nodes[i].x;
      nodes[i].fy = nodes[i].y;
    }
  }
  this.freeAll = function() {
    let nodes = this.kgInfo.nodes;
    context.simulation.alphaTarget(0).alphaDecay(0.03);
    for(let i = 0;i<nodes.length;i++){
      nodes[i].fx = null;
      nodes[i].fy = null;
    }
  }

  /** 固定/释放节点方法 */
  this.changeTypeColor = function(typeIndex, color){
    let _this = this;
    this.typeList[typeIndex].color = color;
    d3.selectAll('circle').style('fill',function(d) {
      if(d.type == _this.typeList[typeIndex].type){
        d.color = color;
        return color;
      }
      return d.color;
    })
  }

  /** 应用/解除事件方法 */
  this.initCurNodeEvent = function(nodes) {
    addNodeEvent("mouseover." + eventName.curNode,assignCurNode,nodes);
    addNodeEvent("mouseleave." + eventName.curNode,clearCurNode,nodes);
  }
  this.initRelatedNodeEvent = function(nodes) {
    addNodeEvent("mouseover." + eventName.relatedNode,showNodeRelated,nodes);
    addNodeEvent("mouseleave." + eventName.relatedNode,showAll,nodes);
  }
  this.clearRelatedNodeEvent = function(nodes) {
    addNodeEvent("mouseover." + eventName.relatedNode,null,nodes);
    addNodeEvent("mouseleave." + eventName.relatedNode,null,nodes);
  }

  /** 初始化绘图画布 */
  function initGraphContainer() {
    let _this = context;
    _this.gcontainer = d3.select(_this.gid);
    if (_this.isFullscreen) {
      _this.width = window.screen.availWidth;
      _this.height = window.screen.availHeight;
    } else {
      _this.width = $("#" + _this.pid).width();
      _this.height = $("#" + _this.pid).height();
    }
    _this.svg = _this.gcontainer.append("svg");
    let sWidth = _this.width;
    let sHeight = _this.height;
    _this.svg.style("background-color", _this.default.BACKGROUND_COLOR);
    _this.svg.attr("width", sWidth);
    _this.svg.attr("height", sHeight-70);
    _this.svg.attr("viewBox", "0 0 " + sWidth / 2 + " " + sHeight / 2);
    _this.svg.attr("id", "svg_idx");
    _this.svg.attr("preserveAspectRatio", "xMidYMidmeet");
    _this.qaGraphLink = _this.svg.append("g").attr("class", "line");
    _this.qaGraphNode = _this.svg.append("g").attr("class", "node");
    _this.qaGraphNodeText = _this.svg.append("g").attr("class", "nodetext");
    _this.nodebuttonGroup = _this.svg.append("g").attr("class", "nodebutton");
    _this.qaGraphLinkText = _this.svg.append("g").attr("class", "linetext");
    _this.svg.on(
      "click",
      function() {
        d3.selectAll(".buttongroup")
          .classed("notshow", true)
          .style("visibility", "hidden");
        // .style("display", "none")
      },
      false
    );
    console.log("--- finish initGraphContainer ---");
  }

  /** 绘制箭头模型方法 */
  function addMaker() {
    let _this = context;
    var arrowMarker = _this.svg
      .append("marker")
      .attr("id", "arrow")
      .attr("markerUnits", "strokeWidth")
      .attr("markerWidth", "12") //
      .attr("markerHeight", "12")
      .attr("viewBox", "0 0 12 12")
      .attr("refX", "38")
      .attr("refY", "6")
      .attr("orient", "auto");
    var arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2"; // 定义箭头形状
    arrowMarker
      .append("path")
      .attr("d", arrowPath)
      .attr("fill", _this.default.MARKER_COLOR);
    console.log("--- finish addMaker ---");
  }

  /** 类型相关操作 */
  // function initTypeList(){
  //   let _this = context;
  //   if(!_this.kgInfo.typeList){
  //     _this.kgInfo.typeList = [];
  //   }
  //   for(let i = 0;i<_this.graph.nodes;i++){
  //     mapToTypeList(_this.graph.nodes[i]);
  //   }
  //
  // }
  function mapToTypeList(node){ //如果类型不存在添加,并最终返回索引
    let _this = context;
    let index = -1;
    for(let i=0;i<_this.typeList.length;i++){
      let typeModal = _this.typeList[i];
      if(node.type == typeModal.type){
        return i;
      }
    }
    if(index == -1){
      _this.typeList.push({type:node.type});
      index = _this.typeList.length - 1;
    }
    return index;
  }

  /** 数据规范化方法 */
  //formatData会直接修改全局kgInfo对象
  function formatData() {
    let _this = context;
    let kg = _this.kgInfo;

    kg.nodes = formatNodes(kg.nodes);
    kg.links = formatLinks(kg.links,kg.nodes);
  }
  function formatNodes(nodes) {
    let _this = context;
    //初始化nodes相关属性
    nodes.forEach(function(n) {
      if (n.center === 1 || n.center === "1") {
        n.fx = _this.width / 2;
        n.fy = _this.height / 2;
      }
      if (typeof n.fx === "undefined" || n.fx === "") {
        n.fx = null;
      }
      if (typeof n.fy === "undefined" || n.fy === "") {
        n.fy = null;
      }
      assignColor(n);
    });
    return nodes;
  }
  function formatLinks(lks,nodes) {
    let links = [];
    lks.forEach(function(m) {
      let sourceNode = nodes.filter(function(n) {
        return n.uuid === m.sId;
      })[0];
      if (!sourceNode) return;
      let targetNode = nodes.filter(function(n) {
        return n.uuid === m.tId;
      })[0];
      if (!targetNode) return;
      links.push({
        source: sourceNode.uuid,
        target: targetNode.uuid,
        uuid: m.uuid,
        sId: m.sId,
        tId: m.tId,
        predicate: m.predicate
      });
    });
    return formatLinksAttr(links);
  }
  function formatLinksAttr(links) {
    let formatLinks = [];
    let linkGroupArr = [];
    links.forEach(function(link) {
      let added = false;
      for (let i = 0; i < linkGroupArr.length; i++) {
        if (
          (linkGroupArr[i][0].source == link.source &&
            linkGroupArr[i][0].target == link.target) ||
          (linkGroupArr[i][0].source == link.target &&
            linkGroupArr[i][0].target == link.source)
        ) {
          linkGroupArr[i].push(link);
          added = true;
          break;
        }
      }
      if (!added) {
        linkGroupArr.push([link]);
      }
    });
    linkGroupArr.forEach(function(group) {
      let groupSource = group[0].sId;
      group.forEach(function(link, index) {
        link.linkDirection = link.source == groupSource;
        link.linkGroupIndex = index + 1;
        link.groupLinkTotal = group.length;
        formatLinks.push(link);
      });
    });
    formatLinks.forEach(function(item) {
      //线的单双，单数且小于2，则只有一条直线，大于2且单数，有多条弧线和一条直线
      let groupLinkTotalHalf = item.groupLinkTotal / 2;
      //能否被2整除，是否有余数，true=有，false=没有，有余数必然有一条直线
      item.groupLinkUneven = item.groupLinkTotal % 2 !== 0;
      //判断是否是最中间的直线（有余数，并且序号刚好是本组连线的一版，这里向上取整）
      item.groupLinkMiddle =
        item.groupLinkUneven === true &&
        Math.ceil(groupLinkTotalHalf) === item.linkGroupIndex;
      //假设有一条直线，index刚好是groupLinkTotalHalf的值，上方的曲线都为0，下方的曲线都为1
      var groupLinkLowerHalf = item.linkGroupIndex <= groupLinkTotalHalf;
      //上方的曲线和直线方向一致，下方的曲线和直线的方向相反
      item.groupLinkArcDirection =
        (groupLinkLowerHalf && item.linkDirection) ||
        (!groupLinkLowerHalf && !item.linkDirection)
          ? 0
          : 1;
      // item.groupLinkArcDirection = 1;
      //都在直线上方，则取当前序号，否则减去中间直线取序号
      item.groupLinkIndexCorrected = groupLinkLowerHalf
        ? item.linkGroupIndex
        : item.linkGroupIndex - Math.ceil(groupLinkTotalHalf);
      item.groupLinkMaxHalf = Math.round(item.groupLinkTotal / 2);
    });
    return formatLinks;
  }

  function removeKgInfoOb(){
    let _this = context;
    let nodesCopy = [];
    let linksCopy = [];
    for(let i=0;i<_this.kgInfo.nodes.length;i++){
      nodesCopy.push(_this.kgInfo.nodes[i]);
    }
    _this.kgInfo.nodes = nodesCopy;
    for(let i=0;i<_this.kgInfo.links.length;i++){
      linksCopy.push(_this.kgInfo.links[i]);
    }
    _this.kgInfo.links = linksCopy;
  }

  this.getTestData = function() {
    return {
      nodeVOS: [
        { id: "1", nodaAtId: "a", label_ZH: "曹操", node_type: "person" },
        { id: "2", nodaAtId: "b", label_ZH: "曹植", node_type: "person" },
        { id: "3", nodaAtId: "c", label_ZH: "曹丕", node_type: "person" },
        { id: "4", nodaAtId: "d", label_ZH: "刘备玄德", node_type: "person" },
        { id: "5", nodaAtId: "e", label_ZH: "魏国", node_type: "country" },
        { id: "6", nodaAtId: "f", label_ZH: "蜀国", node_type: "country" }
      ],
      tripleVOS: [
        { id: "1", predicate: "父亲", s_id: "1", o_id: "2" },
        { id: "12", predicate: "父亲", s_id: "1", o_id: "3" },
        { id: "13", predicate: "君主", s_id: "5", o_id: "1" },
        { id: "14", predicate: "太子", s_id: "3", o_id: "5" },
        { id: "121q5", predicate: "哥哥", s_id: "3", o_id: "2" },
        { id: "61", predicate: "敌对", s_id: "5", o_id: "6" },
        { id: "6aa7", predicate: "接壤", s_id: "5", o_id: "6" },
        { id: "sd62", predicate: "君主", s_id: "6", o_id: "4" }
      ]
    };
  };

  /** 开始受力模型方法 */
  function initSimulation(nodes,links) {
    let _this = context;
    _this.simulation = d3.forceSimulation()
      .force("charge", d3.forceManyBody().strength(-1500))
      .force("link", d3.forceLink()
        .distance(60)
        .id(function(d) {
          return d.uuid;
        })
      )
      .force("collide", d3.forceCollide().strength(-10))
      .force("center", d3.forceCenter(_this.width / 2, _this.height / 2));
    _this.simulation.force(
      "center",
      d3.forceCenter(_this.width / 2, _this.height / 2)
    );
    restartNodesSimulation(nodes,false);
    restartLinksSimulation(links,true);
  }
  function restartNodesSimulation(nodes,restart=true) {
    let _this = context;
    _this.simulation
      .nodes(nodes)
      .alphaTarget(0)
      .alphaDecay(0.05)
      .on("tick", ticked);
    if(restart)_this.simulation.alpha(1).restart();
  }
  function restartLinksSimulation(links,restart=true) {
    let _this = context;
    _this.simulation.force("link").links(links);
    if(restart)_this.simulation.alpha(1).restart();
  }

  /** d3帧调用方法 */
  function ticked() {
    // let _this = context;
    // 更新节点坐标
    context.graphNode
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });
    // 更新文字坐标
    context.graphNodeText
      .attr("x", function(d) {
        return d.x;
      })
      .attr("y", function(d) {
        return d.y;
      });
    // 更新连线坐标
    context.graphLink.attr("d", function(d) {
      var dx = d.target.x - d.source.x,
        dy = d.target.y - d.source.y,
        dr = Math.sqrt(dx * dx + dy * dy),
        unevenCorrection = d.groupLinkUneven ? 0 : 0.2;
      var curvature = 2,
        arc =
          (1.0 / curvature) *
          ((dr * d.groupLinkMaxHalf) /
            (d.groupLinkIndexCorrected - unevenCorrection));
      if (d.groupLinkMiddle) {
        arc = 0;
      }
      var dd =
        "M" +
        d.source.x +
        "," +
        d.source.y +
        "A" +
        arc +
        "," +
        arc +
        " 0 0," +
        d.groupLinkArcDirection +
        " " +
        d.target.x +
        "," +
        d.target.y;
      return dd;
    });
    // 刷新连接线上的文字位置
    context.graphLinkText
      .attr("x", function(d) {
        if (!d.source.x || !d.target.x) return 0;
        return (parseFloat(d.source.x) + parseFloat(d.target.x)) / 2;
      })
      .attr("y", function(d) {
        if (!d.source.y || !d.target.y) return 0;
        return (parseFloat(d.source.y) + parseFloat(d.target.y)) / 2;
      });
    // 更新节点操作按钮组坐标
    context.graphNodeButtonGroup
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
      .attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ") scale(1)";
      });
  }

  /** 元素绘制方法 */
  function drawNode(nodes) {
    let _this = context;
    let node = _this.qaGraphNode.selectAll("circle").data(nodes, function(d) {
      return d.uuid;
    });
    node.exit().remove();
    let nodeEnter = node.enter().append("circle");

    //初始化节点事件
    _this.initRelatedNodeEvent(nodeEnter);
    _this.initCurNodeEvent(nodeEnter);
    addNodeEvent("click", switchButtonShow,nodeEnter);
    addNodeEvent("dblclick",function() {
      event.preventDefault();
    },nodeEnter)
    addNodeEvent("mouseenter",function() {
      d3.select(this)
        .style("stroke-width", "6")
        .style("cursor","pointer");
    },nodeEnter)

    nodeEnter.call(
      d3
        .drag()
        .on("start", dragStarted)
        .on("drag", dragged)
        .on("end", dragEnded)
    );
    node = nodeEnter.merge(node).text(function(d) {
      return d.label;
    });
    node.style("fill", function(d) {
      return d.color;
    });
    node.style("stroke", _this.default.NODE_STROKE_COLOR);
    node.style("stroke-opacity", 0.6);
    node.style("stroke-width", 5);
    node.attr("r", _this.default.R);
    node.attr("uuid", function(d) {
      return d.uuid;
    });
    node
      .append("title") // 为每个节点设置title
      .text(function(d) {
        return d.label;
      });
    _this.graphNode = node;
  }
  function drawNodeText(nodes) {
    let _this = context;
    let nodetext = _this.qaGraphNodeText
      .selectAll("text")
      .data(nodes, function(d) {
        return d.uuid;
      });
    nodetext.exit().remove();
    let nodetextEnter = nodetext.enter().append("text");
    nodetextEnter.call(
      d3
        .drag()
        .on("start", dragStarted)
        .on("drag", dragged)
        .on("end", dragEnded)
    );
    nodetext = nodetextEnter.merge(nodetext).text(function(d) {
      return d.label;
    });
    nodetext
      .style("fill", _this.default.FONT_COLOR)
      .style("pointer-events", "none") //取消鼠标对文字的感知
      .style("font-weight", function(d) {
        return fontWeight(d);
      })
      .attr("class", "nodetext")
      .attr("font-family", _this.default.FONT_FAMILY)
      .attr("text-anchor", "middle")
      .attr("dy", function(d) {
        return fontDy(d);
      })
      .attr("font-size", function(d) {
        return fontSize(d);
      })
      .attr("uuid", function(d) {
        return d.uuid;
      })
      .text(function(d) {
        return d.label;
      });
    nodetext
      .append("title") // 为每个节点设置title
      .text(function(d) {
        if (d.label) {
          return d.label;
        }
        return "";
      });
    _this.graphNodeText = nodetext;
  }
  function drawLink(links) {
    let _this = context;
    let link = _this.qaGraphLink.selectAll(".link").data(links, function(d) {
      return d.uuid;
    });
    link.exit().remove();
    let linkEnter = link
      .enter()
      .append("path")
      .attr("class", "link")
      .attr("stroke-width", 1)
      .attr("stroke", _this.default.MARKER_COLOR)
      .attr("fill", "none")
      .attr("uuid", function(d) {
        return d.uuid;
      })
      .attr("id", function(d) {
        return "invis_" + d.sId + "-" + d.uuid + "-" + d.tId;
      })
      .attr("marker-end", "url(#arrow)") // 箭头
    addLinkEvent("mouseover.display",function() {
      d3.select(this)
        .style("stroke-width", "2")
        .style("cursor","pointer");
    },linkEnter)
    addLinkEvent("mouseleave.display",function() {
      d3.select(this).style("stroke-width", "1");
    },linkEnter)
    link = linkEnter.merge(link);
    _this.graphLink = link;
  }
  function drawLinkText(links) {
    let _this = context;
    var linktext = _this.qaGraphLinkText
      .selectAll(".linktext")
      .data(links, function(d) {
        return d.uuid;
      });
    linktext.exit().remove();
    var linktextEnter = linktext
      .enter()
      .append("text")
      .attr("class", "linktext")
      .append("textPath")
      .attr("startOffset", "50%")
      .attr("text-anchor", "middle")
      .attr("uuid", function(d) {
        return d.uuid;
      })
      .attr("xlink:href", function(d) {
        return (
          "#invis_" + d.sId + "-" + d.uuid + "-" + d.tId
        );
      })
      .style("fill", _this.default.LINK_FONT_COLOR)
      .style("font-size", "10px")
      .text(function(d) {
        return d.predicate;
      });
    linktext = linktextEnter.merge(linktext).text(function(d) {
      return d.predicate;
    });
    _this.graphLinkText = linktext;
  }
  function drawButtonGroup(nodes) {
    let _this = context;
    d3.selectAll(".nodebutton")
      .selectAll("use")
      .remove();
    // d3.selectAll(".nodebutton >g").remove();
    let nodebutton = _this.nodebuttonGroup
      .selectAll(".nodebutton")
      .data(nodes, function(d) {
        return d.uuid;
      });
    nodebutton.exit().remove();
    let nodebuttonEnter = nodebutton
      .enter()
      .append("use") //  为每个节点组添加一个 use 子元素
      .attr("r", function(d) {
        if (!d.r) {
          return _this.defaultR;
        }
        return d.r;
      })
      .attr("uuid", function(d) {
        return d.uuid;
      })
      .attr("xlink:href", function(d) {
        if (!d.r) {
          return "#out_circle_" + _this.default.R;
        }
        return "#out_circle_" + d.r;
      }) //  指定 use 引用的内容
      .attr("class", function(d) {
        return "buttongroup out_buttongroup_" + d.uuid;
      })
      .classed("notshow", true)
      .style("visibility", "hidden");
    nodebutton = nodebuttonEnter.merge(nodebutton);
    _this.graphNodeButtonGroup = nodebutton;
  }
  function drawToolButton() {
    var _this = context;
    //先删除所有为节点自定义的按钮组
    d3.selectAll("svg >defs").remove();
    var nodes = _this.kgInfo.nodes;
    var pie = d3.pie().value(function(d) {
      return d.value; //处理数据，指定value作为计算比例的字段
    });
    var piedata = pie(_this.toolbarData);
    var nodeButtonGroup = _this.svg.append("defs");
    var nodeRArr = [];
    nodes.forEach(function(m) {
      if (!m.r) {
        m.r = _this.default.R;
      }
      //按半径分别定义每种按钮组的图标
      if (nodeRArr.indexOf(m.r) == -1) {
        nodeRArr.push(m.r);
        var nbtng = nodeButtonGroup.append("g").attr("id", "out_circle_" + m.r); //为每一个节点定制一个按钮组，在画按钮组的时候为其指定该id
        var buttonGroupEnter = nbtng
          .selectAll(".buttongroup")
          .data(piedata)
          .enter()
          .append("g")
          .attr("class", function(d) {
            return "action_" + d.data.code;
          });
        var arc = d3
          .arc()
          .innerRadius(m.r)
          .outerRadius(m.r + 30);
        buttonGroupEnter
          .append("path")
          .attr("d", function(d) {
            return arc(d);
          })
          .attr("fill", _this.default.BTN_COLOR)
          .style("opacity", 0.6)
          .attr("stroke", _this.default.BTN_STROKE_COLOR)
          .attr("stroke-width", 1);
        buttonGroupEnter
          .append("text")
          .attr("transform", function(d) {
            return "translate(" + arc.centroid(d) + ")";
          })
          .attr("text-anchor", "middle")
          .text(function(d) {
            return d.data.name;
          })
          .style("fill", function() {
            return "#fff";
          })
          .attr("font-size", 10);
      }
    });
  }

  /** 为节点绑定事件汇总方法 */
  function addNodeEvent(eventName, callback, nodes) {
    if(!nodes){
      nodes = d3.selectAll('circle');
    }
    nodes.on(eventName,callback);
  }
  function addLinkEvent(eventName, callback, links) {
    if(!links){
      links = d3.selectAll('path');
    }
    links.on(eventName,callback);
  }

  /** 根据id获取对象数组的某一元素 对象必须含有id属性 */
  function getArrItemByID(id, arr) {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].uuid == id) {
        return arr[i];
      }
    }
    return null;
  }

  function bindEventButtonGroup() {
    var _this = context;
    //按钮组事件绑定
    _this.toolbarData.forEach(function(m) {
      var btnClass = ".action_" + m.code;
      _this.svg.selectAll(btnClass).on("click", function() {
        // console.log(
        //   d.data.name + ":" + d.data.code + ":uuid:" + _this.selectUuid
        // );
      });
    });
  }

  /** 显示刷线方法 */
  // eslint-disable-next-line no-unused-vars
  function refresh() {
    context.svg.call(context.zoom.transform, d3.zoomIdentity);
  }
  function refreshShow() {
    zoomIn();
    zoomOut();
    showAll();
  }

  /** 缩放方法 */
  function zoomed() {
    d3.selectAll(".node").attr("transform", d3.event.transform);
    d3.selectAll(".nodetext text").attr("transform", d3.event.transform);
    d3.selectAll(".line").attr("transform", d3.event.transform);
    d3.selectAll(".linetext text").attr("transform", d3.event.transform);
    d3.selectAll(".nodebutton").attr("transform", d3.event.transform);
    //_this.svg.selectAll("g").attr("transform", d3.event.transform);
  }
  function zoomClick(direction) {
    let self = context;
    let factor = 0.2;
    let targetZoom = 1;
    let extent = self.zoom.scaleExtent();
    targetZoom = 1 + factor * direction;
    if (targetZoom < extent[0] || targetZoom > extent[1]) {
      return false;
    }
    self.zoom.scaleBy(self.svg, targetZoom); // 执行该方法后 会触发zoom事件
  }
  function zoomIn() {
    zoomClick(1);
  }
  function zoomOut() {
    zoomClick(-1);
  }

  /** 拖动方法 */
  function dragStarted(d) {
    if (!d3.event.active) context.simulation.alphaTarget(0.8).restart();
    d.fx = d.x;
    d.fy = d.y;
  }
  function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }
  function dragEnded(d) {
    if (!d3.event.active) context.simulation.alphaTarget(0);
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  /** 力导图显示操作 主要用于事件函数调用 */
  function showAll() {
    d3.select(this).style("stroke-width", 2);
    //todo其他节点和连线一并显示
    d3.select(".node").style("fill-opacity", 1);
    d3.select(".nodetext").style("fill-opacity", 1);
    d3.selectAll(".link")
      .style("stroke-opacity", 1)
      .attr("marker-end", "url(#arrow)");
    d3.selectAll(".linktext").style("fill-opacity", 1);
  } //显示所有图元
  function showNodeRelated(d) {
    let _this = context;
    //todo鼠标放上去只显示相关节点，其他节点和连线隐藏
    d3.selectAll(".node").style("fill-opacity", 0.1);
    let relvantNodeIds = [];
    let links = _this.kgInfo.links;
    var relvantNodes = links.filter(function(n) {
      return n.sId === d.uuid || n.tId === d.uuid;
    });
    relvantNodes.forEach(function(item) {
      relvantNodeIds.push(item.sId);
      relvantNodeIds.push(item.tId);
    });
    //显示相关的节点
    _this.qaGraphNode.selectAll("circle").style("fill-opacity", function(c) {
      if (relvantNodeIds.indexOf(c.uuid) > -1 || c.uuid === d.uuid) {
        return 1.0;
      }
    });
    //透明所有节点文字
    d3.selectAll(".nodetext").style("fill-opacity", 0.1);
    //显示相关的节点文字
    _this.qaGraphNodeText.selectAll("text").style("fill-opacity", function(c) {
      if (relvantNodeIds.indexOf(c.uuid) > -1 || c.uuid === d.uuid) {
        return 1.0;
      }
    });
    //透明所有连线、箭头
    d3.selectAll(".line")
      .style("stroke-opacity", 0.1)
      .selectAll("path")
      .attr("marker-end", "none");
    //显示相关的连线
    _this.qaGraphLink
      .selectAll(".link")
      .style("stroke-opacity", function(c) {
        if (c.tId === d.uuid || c.sId === d.uuid) {
          return 1.0;
        }
      })
      .attr("marker-end", function(c) {
        //展示相关箭头
        if (c.tId === d.uuid || c.sId === d.uuid) {
          return "url(#arrow)";
        }
      });
    //透明所有连线文字
    d3.selectAll(".linetext").style("fill-opacity", 0.1);
    //显示相关的连线文字
    _this.qaGraphLinkText
      .selectAll(".linktext")
      .style("fill-opacity", function(c) {
        if (c.tId === d.uuid || c.sId === d.uuid) {
          return 1.0;
        }
      });
  } //展示与节点d相关的节点
  function switchButtonShow(d) {
    let _this = context;
    _this.selectUuid = d.uuid;
    var out_buttongroup_id = ".out_buttongroup_" + d.uuid;
    var selectItem = d3.select(out_buttongroup_id)._groups[0][0];
    if (selectItem.classList.contains("notshow")) {
      _this.svg.selectAll(".buttongroup").classed("notshow", true);
      d3.select(out_buttongroup_id)
        .classed("notshow", false)
        .style("visibility", "hidden")
        .style("translate");
      // .style("display", "none")
    } else {
      d3.select(out_buttongroup_id)
        .classed("notshow", true)
        .style("visibility", "visible");
      // .style("display", "block")
    }
    event.stopPropagation();
  } //切换按钮展示与关闭
  function assignCurNode(d) {
    let _this = context;
    _this.curNode.valid = true;
    _this.curNode.uuid = d.uuid;
    _this.curNode.uri = d.uri;
    _this.curNode.label = d.label;
    _this.curNode.type = d.type;
  } //获取当前节点信息
  function clearCurNode() {
    context.curNode.valid = false;

  } //清除当前节点信息

  /** 字体、颜色等属性计算函数 */
  function fontSize(d) {
    let _this = context;
    if (d.font_size == -1 || d.font_size == undefined) {
      // 根据半径计算字体大小
      d.font_size = ((d.r ? d.r : _this.default.R) * 3) / (d.label.length * 1.5);
      if(d.label.length <= 2) d.font_size -= 3;
      let limit = Number(d.r) - 15;
      d.font_size = d.font_size > limit ? limit : d.font_size;
    }
    return d.font_size;
  }
  function fontWeight(d) {
    return Math.floor(900 / fontSize(d) + 500);
  }
  function fontDy(d) {
    return String(fontSize(d) / 2 - 2);
  }
  function assignColor(node) {
    // 自定义节点颜色
    if(node.selfColor != undefined) {
      node.color = node.selfColor;
    }

    let _this = context;

    let index = mapToTypeList(node);

    if(_this.typeList[index].color){ //优先使用type携带颜色
      node.color = _this.typeList[index].color;
    }
    else if(index < _this.colorList.length) { //次选colorList中定义的默认颜色
      _this.typeList[index].color = _this.colorList[index];
      node.color = _this.colorList[index];
    }
    else { //最次生成随机颜色
      let color = randomColor();
      node.color = color;
      _this.typeList[index].color = color
    }
  }
  function randomColor() {
    return "#"+Math.floor(Math.random()*16777215).toString(16);
  }
}

/**
 * 上下文
 * 在有些方法调用中(如drag)this指向的是d3调用元素，无法获取KgPainter中定义的一些属性
 * 因而在初始化时将context指向KgPainter本身来获取相关属性
 * @type {{}}
 */
var context = {}; // == this
